#include "fileperf.hh"
#include <iostream>
#include <cassert>
#include <stdio.h>

#define EPSILON 0.0001

static unsigned int blockSize;
static unsigned int pointerSize;
static unsigned int maximalSize;
static storageType type;
static std::vector<unsigned int> fileSizes;


void print(outputStruct *output){
	double access =  double(output->averageAccessNum);
	long unsigned int space = output->space;

	std::cout << "Average access: " ;
	printf("%f",access);
	std::cout <<std::endl;
	std::cout << "Space: ";
	printf("%lu",space);
	std::cout <<std::endl;
}

bool equalsUpToEpsilon(double a, double b) {
	//cout<<"equals? "<<a<<","<<b<<endl;
	return (a>b) ? (a-b<=EPSILON) : (b-a<=EPSILON);
}

void check(bool fail, unsigned int expectedSpace, double expectedAvg){
	int retVal;
	outputStruct output;
	retVal = fileSystemPerformance(blockSize, pointerSize, maximalSize, type, fileSizes,
			&output);
	if(retVal == 0 && !fail){
		print(&output);
		assert(equalsUpToEpsilon(output.space,expectedSpace));
		assert(equalsUpToEpsilon(output.averageAccessNum,expectedAvg));
	}
	else if(retVal == 0 && fail){
		std::cout << "Error: should have failed" << std::endl;
	}
	else if(fail && retVal == -1){
		std::cout << "Returned error code as expected" << std::endl;
	}
	else std::cout << "Error: failed" << std::endl;
}



int main(){

	/////////////////////////////////////BASIC LINKED LIST/////////////////////////////////////
	//Test for linked list:
	std::cout << "Now testing linked list:" << std::endl;
	std::cout <<  std::endl;
	std::cout <<  std::endl;

	type = FS_LINKED_LIST;

	//Check for bad parameters
	//blockSize = 0;
	//pointerSize = 0;
	//maximalSize = 0;
	//check(true,0,0);

	//Should fail because blockSize<pointerSize
	pointerSize = 2;
	maximalSize = 2;
	blockSize = 1;
	check(true,0,0);

	fileSizes.push_back(30);
	blockSize = 20;
	pointerSize = 2;
	maximalSize = 120;

	check(false,40,1.4);

	//Push another file
	fileSizes.push_back(30);
	check(false,80,1.4);

	//Push file larger than max file
	fileSizes.push_back(121);
	check(true,0,0);

	//Push file larger than max file
	fileSizes.pop_back();
	fileSizes.push_back(120);
	check(false,220,2.21667);

	//push lots of files
	fileSizes.push_back(46);
	fileSizes.push_back(24);
	fileSizes.push_back(77);
	fileSizes.push_back(94);
	fileSizes.push_back(33);
	fileSizes.push_back(85);
	fileSizes.push_back(63);
	fileSizes.push_back(76);
	fileSizes.push_back(42);
	fileSizes.push_back(44);
	fileSizes.push_back(99);

	check(false,1100,2.25214);

	//Change parameters and retest
	blockSize = 30;
	pointerSize = 5;
	maximalSize = 180;

	check(false,1230,1.77127);

	/////////////////////////////////////BASIC INODE/////////////////////////////////////
	std::cout <<  std::endl;
	std::cout << "passed basic linked list! " << std::endl;
	std::cout <<  std::endl;
	std::cout << "Now testing inode:" << std::endl;
	std::cout <<  std::endl;


	type = FS_INODE;
	fileSizes.clear();
	fileSizes.push_back(30);
	check(false,60,2);

	maximalSize = 1000;
	fileSizes.push_back(930);
	fileSizes.push_back(20);
	check(false,1260,2.51613);


	fileSizes.push_back(1001);
	check(true,0,0);
	fileSizes.pop_back();

	//Push lots of files:
	fileSizes.push_back(462);
	fileSizes.push_back(24);
	fileSizes.push_back(771);
	fileSizes.push_back(94);
	fileSizes.push_back(33);
	fileSizes.push_back(853);
	fileSizes.push_back(63);
	fileSizes.push_back(76);
	fileSizes.push_back(428);
	fileSizes.push_back(44);
	fileSizes.push_back(999);

	check(false,6330,2.58575);

	//Change parameters and retest
	blockSize = 41;
	pointerSize = 3;

	check(false,5822,2.12597);

	/////////////////////////////////////OLD TESTS/////////////////////////////////////

	//CHECKS FROM 2 YEARS AGO
	std::cout <<  std::endl;
	std::cout << "passed basic inode! " << std::endl;
	std::cout <<  std::endl;
	std::cout << "Old checks: " << std::endl;
	std::cout <<  std::endl;

	//////////
	puts("LinkedList: 64, 1. iNode: 128, 2 //(code = 'test_1')");
	fileSizes.clear();
	blockSize=64;
	pointerSize=4;
	maximalSize=64;
	fileSizes.push_back(16);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,128, 2);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,64, 1);

	//////////
	puts("\nLinkedList: 96, 4.2666. iNode: 84, 2.6 //(code = 'test_2')");//TODO
	fileSizes.clear();
	blockSize=12;
	pointerSize=4;
	maximalSize=60;
	fileSizes.push_back(60);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,84, 2.6);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,96, 4.2666);

	//////////
	puts("\nLinkedList: 48, 2.4. iNode: 60, 2.2 //(code = 'test_3')");//TODO
	fileSizes.clear();
	blockSize=12;
	pointerSize=4;
	maximalSize=60;
	fileSizes.push_back(30);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,60, 2.2);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,48, 2.4);

	//////////
	puts("\nLinkedList: 60, 2.4. iNode: 72, 2.2 //(code = 'test_4')");//TODO
	fileSizes.clear();
	blockSize=12;
	pointerSize=4;
	maximalSize=60;
	fileSizes.push_back(30);
	fileSizes.push_back(0);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,72, 2.2);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,60, 2.4);

	//////////
	puts("\nLinkedList: 2048, 1.0039. iNode: 2048, 2 //(code = 'test_5a')");
	fileSizes.clear();
	blockSize=1024;
	pointerSize=4;
	maximalSize=4096;
	fileSizes.push_back(1024);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,2048, 2);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,2048, 1.0039);

	//////////
	puts("\nLinkedList: 576, 4.78125. iNode: 576, 2 //(code = 'test_5b')");
	fileSizes.clear();
	blockSize=64;
	pointerSize=4;
	maximalSize=268435456;
	fileSizes.push_back(512);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,576, 2);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,576, 4.78125);

	/////////
	puts("\nLinkedList: 2240, 17.5683. iNode: 2304, 2.875 //(code = 'test_5c')");
	fileSizes.clear();
	blockSize=64;
	pointerSize=4;
	maximalSize=268435456;
	fileSizes.push_back(2048);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,2304, 2.875);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,2240, 17.5683);

	//////////
	puts("\nLinkedList: 35008, 273.5667. iNode: 35072, 4.3789 //(code = 'test_5d')");

	fileSizes.clear();
	blockSize=64;
	pointerSize=4;
	maximalSize=268435456;
	fileSizes.push_back(32768);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,35072, 4.3789);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,35008, 273.5667);

	//////////
	puts("\nLinkedList: 17895744, 139810.6333. iNode: 17895808, 6.7153 //(code = 'test_5e')");
	fileSizes.clear();
	blockSize=64;
	pointerSize=4;
	maximalSize=268435456;
	fileSizes.push_back(16777216);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,17895808, 6.7153 );

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,17895744, 139810.6333);

	//////////
	puts("\nLinkedList: 402653184, 16777216.5. iNode: -1 //(code = 'test_6')");
	fileSizes.clear();
	blockSize=12;
	pointerSize=4;
	maximalSize=268435456;
	fileSizes.push_back(268435456);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(true,2048, 2);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,402653184, 16777216.5);

	//////////
	puts("\nLinkedList: 791808, 618.7910. iNode: 792576, 3.1652 //(code = 'test_7')");
	fileSizes.clear();
	blockSize=64;
	pointerSize=4;
	maximalSize=16777216;
	fileSizes.push_back(500);
	fileSizes.push_back(195);
	fileSizes.push_back(430);
	fileSizes.push_back(433);
	fileSizes.push_back(340);
	fileSizes.push_back(6879);
	fileSizes.push_back(5551);
	fileSizes.push_back(28745);
	fileSizes.push_back(643987);
	fileSizes.push_back(54879);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,792576, 3.1652 );

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,791808, 618.7910);

	//////////
	puts("\nLinkedList: 19757, 53.9182. iNode: 20654, 3.6600 //(code = 'test_8')");
	fileSizes.clear();
	blockSize=23;
	pointerSize=4;
	maximalSize=16384;
	fileSizes.push_back(340);
	fileSizes.push_back(656);
	fileSizes.push_back(5);
	fileSizes.push_back(130);
	fileSizes.push_back(0);
	fileSizes.push_back(130);
	fileSizes.push_back(576);
	fileSizes.push_back(5005);
	fileSizes.push_back(0);
	fileSizes.push_back(9382);

	type = FS_INODE;
	std::cout << "For inode: " << std::endl;
	check(false,20654, 3.6600);

	type = FS_LINKED_LIST;
	std::cout << "For linked list: " << std::endl;
	check(false,19757, 53.9182);

	std::cout << std::endl;
	std::cout << "PASSED ALL! :)" << std::endl;
	return 0;
}

//////////////////////////////////////////////////END///////////////////////////////////

